/*
 ------------------------------------------------------------------------------
 Copyright (C) 2013 Eternal Games.

 This file is part of the EternalTech source code.

 The EternalTech source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The EternalTech source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the EternalTech source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


//
// cg_main.cpp - player view drawing
//


#include "cg_local.h"


/*
 ==============================================================================

 VIEW RENDERING

 ==============================================================================
*/


/*
 ==================
 CG_SetupScreenRect
 ==================
*/
static void CG_SetupScreenRect (){

	cg.renderView.width = SCREEN_WIDTH * cg_viewSize->integerValue / 100;
	cg.renderView.width &= ~1;

	cg.renderView.height = SCREEN_HEIGHT * cg_viewSize->integerValue / 100;
	cg.renderView.height &= ~1;

	cg.renderView.x = (SCREEN_WIDTH - cg.renderView.width) >> 1;
	cg.renderView.y = (SCREEN_HEIGHT - cg.renderView.height) >> 1;

	cg.renderView.horzAdjust = H_SCALE;
	cg.renderView.horzPercent = 1.0f;

	cg.renderView.vertAdjust = V_SCALE;
	cg.renderView.vertPercent = 1.0f;
}

/*
 ==================
 CG_SetupViewOriginAndAngles
 ==================
*/
static void CG_SetupViewOriginAndAngles (){

	float	f;
	int		timeDelta;

	cg.renderViewAngles = cg.predictedPlayerState.viewAngles;

	cg.renderView.origin = cg.predictedPlayerState.origin;
	cg.renderView.origin.z += cg.predictedPlayerState.viewHeight;

	cg.renderView.axis = cg.renderViewAngles.ToMat3();

	// Smooth out view height changes
	if (cg.viewHeightDelta){
		timeDelta = cg.time - cg.viewHeightTime;

		if (timeDelta < 0)
			cg.viewHeightTime = cg.time;

		if (timeDelta < 250)
			cg.renderView.origin.z -= cg.viewHeightDelta * (250 - timeDelta) / 250;
	}

	// Add prediction error decay
	if (cg_predictionErrorDecay->integerValue){
		f = (float)(cg_predictionErrorDecay->integerValue - (cg.time - cg.predictedErrorTime)) / cg_predictionErrorDecay->integerValue;

		if (f <= 0.0f || f >= 1.0f)
			cg.predictedErrorTime = 0;
		else
			cg.renderView.origin += cg.predictedError * f;
	}
}

/*
 ==================
 CG_SetupFOV
 ==================
*/
static void CG_SetupFOV (){

	float	f;

	cg.renderView.fovX = cg_fov->floatValue;

	if (cg.zoomed){
		f = (cg.time - cg.zoomTime) / 150.0f;

		if (f > 1.0f)
			cg.renderView.fovX = 25.0f;
		else
			cg.renderView.fovX = cg.renderView.fovX + f * (25.0f - cg.renderView.fovX);
	}
	else {
		f = (cg.time - cg.zoomTime) / 150.0f;

		if (f > 1.0f)
			cg.renderView.fovX = cg.renderView.fovX;
		else
			cg.renderView.fovX = 25.0f + f * (cg.renderView.fovX - 25.0f);
	}

	f = cg.renderView.width / glqMath::Tan(cg.renderView.fovX / 360.0f * M_PI);
	cg.renderView.fovY = glqMath::ATan(cg.renderView.height, f) * 360.0f / M_PI;

	cg.renderView.fovScale = cg.renderView.fovX * (1.0f / DEFAULT_FOV);
}

/*
 ==================
 CG_SetupRenderView
 ==================
*/
static void CG_SetupRenderView (){

	// Set up screen rect
	CG_SetupScreenRect();

	// Set up view origin and angles
	CG_SetupViewOriginAndAngles();

	// Set up field of view
	CG_SetupFOV();

	// Set the time
	cg.renderView.time = cg.time;
}

/*
 ==================
 CG_SetupSoundListener
 ==================
*/
static void CG_SetupSoundListener (){

	soundListener_t	soundListener;
	bool			underwater;

	if (CG_Contents(cg.renderView.origin, bounds_zero, MASK_LIQUID, true, ENTITYNUM_NONE) & MASK_LIQUID)
		underwater = true;
	else
		underwater = false;

	soundListener.listenerId = cg.predictedPlayerState.clientNum;
	soundListener.origin = cg.renderView.origin;
	soundListener.axis = cg.renderView.axis;
	soundListener.underwater = underwater;
	soundListener.velocity = cg.predictedPlayerState.velocity;
	soundListener.time = cg.time;

	glqSound->PlaceListener(&soundListener);
}

/*
 ==================
 CG_SetupShakeAngles
 ==================
*/
static void CG_SetupShakeAngles (){

	float		amplitude;
	glqAngles	shakeAngles;

	amplitude = glqSound->ShakeAmplitudeForListener();

	shakeAngles.pitch = game.random.CRandFloat() * amplitude;
	shakeAngles.yaw = game.random.CRandFloat() * amplitude;
	shakeAngles.roll = 0.0f;

	etClient->SetJitterAngles(shakeAngles.pitch, shakeAngles.yaw);		// TODO: also add kickAngles
}

/*
 ==================
 CG_RenderView
 ==================
*/
void CG_RenderView (){

	// Set up the render view
	CG_SetupRenderView();

	// Add the view weapon
	CG_AddViewWeapon(&cg.predictedPlayerState, &cg.predictedPlayerEntity);

	// Update test utilities
	CG_UpdateTestModel();
	CG_UpdateTestSound();

	// Set up the sound listener
	CG_SetupSoundListener();

	// Set up the shake angles
	CG_SetupShakeAngles();

	// Set mouse sensitivity scale based on the FOV scale
	etClient->SetSensitivityScale(cg.renderView.fovScale);

	// Allow view size changes
	if (cg_viewSize->integerValue < 100){
		glqRenderer->SetColor1(0.0f);
		glqRenderer->DrawStretchPic(0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT, 0.0f, 0.0f, 1.0f, 1.0f, H_SCALE, 1.0f, V_SCALE, 1.0f, cgs.whiteMaterial);
	}

	// Render the scene
	glqRenderer->RenderScene(cgs.scene, &cg.renderView, true);
}